package org.apache.solr.dataimport.handler.scheduler;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpPostScheduler extends TimerTask {
	private String syncEnabled;
	private String[] syncCores;
	private String server;
	private String port;
	private String webapp;
	private String params;
	private String interval;
	private String cores;
	private SolrDataImportProperties p;
	private boolean singleCore;

	private static final Logger logger = LoggerFactory
			.getLogger(HttpPostScheduler.class);

	public HttpPostScheduler(String webAppName, String autoDeltaImportConfPath) throws Exception {
		// load properties from global dataimport.properties
		p = new SolrDataImportProperties(autoDeltaImportConfPath);
		reloadParams();
		fixParams(webAppName);

		if (!syncEnabled.equals("1"))
			throw new Exception("Schedule disabled");

		if (syncCores == null
				|| (syncCores.length == 1 && syncCores[0].isEmpty())) {
			singleCore = true;
			logger.info("<index update process> Single core identified in dataimport.properties");
		} else {
			singleCore = false;
			logger.info("<index update process> Multiple cores identified in dataimport.properties. Sync active for: {}", cores);
		}
	}

	public HttpPostScheduler(SolrDataImportProperties p) throws Exception {
		// load properties from global dataimport.properties
		this.p = p;
		loadParams();
		if (!syncEnabled.equals("1"))
			throw new Exception("Schedule disabled");
		
		if (syncCores == null
				|| (syncCores.length == 1 && syncCores[0].isEmpty())) {
			singleCore = true;
			logger.info("<index update process> Single core identified in dataimport.properties");
		} else {
			singleCore = false;
			logger.info("<index update process> Multiple cores identified in dataimport.properties. Sync active for: {}", cores);
		}
	}
	
	private void loadParams() {
		syncEnabled = p.getProperty(SolrDataImportProperties.SYNC_ENABLED);
		cores = p.getProperty(SolrDataImportProperties.SYNC_CORES);
		server = p.getProperty(SolrDataImportProperties.SERVER);
		port = p.getProperty(SolrDataImportProperties.PORT);
		webapp = p.getProperty(SolrDataImportProperties.WEBAPP);
		params = p.getProperty(SolrDataImportProperties.PARAMS);
		interval = p.getProperty(SolrDataImportProperties.INTERVAL);
		syncCores = cores != null ? cores.split(",") : null;
	}

	private void reloadParams() {
		p.loadProperties(true);
		syncEnabled = p.getProperty(SolrDataImportProperties.SYNC_ENABLED);
		cores = p.getProperty(SolrDataImportProperties.SYNC_CORES);
		server = p.getProperty(SolrDataImportProperties.SERVER);
		port = p.getProperty(SolrDataImportProperties.PORT);
		webapp = p.getProperty(SolrDataImportProperties.WEBAPP);
		params = p.getProperty(SolrDataImportProperties.PARAMS);
		interval = p.getProperty(SolrDataImportProperties.INTERVAL);
		syncCores = cores != null ? cores.split(",") : null;
	}

	private void fixParams(String webAppName) {
		if (server == null || server.isEmpty())
			server = "localhost";
		if (port == null || port.isEmpty())
			port = "8080";
		if (webapp == null || webapp.isEmpty())
			webapp = webAppName;
		if (interval == null || interval.isEmpty() || getIntervalInt() <= 0)
			interval = "30";
	}

	@Override
	public void run() {
		try {
			// check mandatory params
			if (server.isEmpty() || webapp.isEmpty() || params == null
					|| params.isEmpty()) {
				logger.warn("<index update process> Insuficient info provided for data import");
				logger.info("<index update process> Reloading global dataimport.properties");
				reloadParams();

				// single-core
			} else if (singleCore) {
				prepUrlSendHttpPost();

				// multi-core
			} else if (syncCores.length == 0
					|| (syncCores.length == 1 && syncCores[0].isEmpty())) {
				logger.warn("<index update process> No cores scheduled for data import");
				logger.info("<index update process> Reloading global dataimport.properties");
				reloadParams();

			} else {
				for (String core : syncCores) {
					prepUrlSendHttpPost(core);
				}
			}
		} catch (Exception e) {
			logger.error("Failed to prepare for sendHttpPost", e);
			reloadParams();
		}
	}

	private void prepUrlSendHttpPost() {
		String coreUrl = "http://" + server + ":" + port + "/" + webapp
				+ params;
		sendHttpPost(coreUrl, null);
	}

	private void prepUrlSendHttpPost(String coreName) {
		String coreUrl = "http://" + server + ":" + port + "/" + webapp + "/"
				+ coreName + params;
		sendHttpPost(coreUrl, coreName);
	}

	private void sendHttpPost(String completeUrl, String coreName) {
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
		Date startTime = new Date();

		// prepare the core var
		String core = coreName == null ? "" : "[" + coreName + "] ";

		logger.info("{}<index update process> Process started at .............. {}", core, df.format(startTime));

		try {

			URL url = new URL(completeUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();

			conn.setRequestMethod("POST");
			conn.setRequestProperty("type", "submit");
			conn.setRequestProperty("Content-Type", "*/*");
			conn.setDoOutput(true);

			// Send HTTP POST
			conn.connect();

			logger.info("{}<index update process> Request method\t\t\t{}", core, conn.getRequestMethod());
			logger.info("{}<index update process> Succesfully connected to server\t{}", core, server);
			logger.info("{}<index update process> Using port\t\t\t{}", core, port);
			logger.info("{}<index update process> Application name\t\t\t{}", core, webapp);  
			logger.info("{}<index update process> URL params\t\t\t{}", core, params);  
			logger.info("{}<index update process> Full URL\t\t\t\t{}", core, conn.getURL());
			logger.info("{}<index update process> Response message\t\t\t{}", core, conn.getResponseMessage());
			logger.info("{}<index update process> Response code\t\t\t", core, conn.getResponseCode());

			// listen for change in properties file if an error occurs
			if (conn.getResponseCode() != 200) {
				reloadParams();
			}

			conn.disconnect();
			logger.info("{}<index update process> Disconnected from server\t\t{}", core, server);
			Date endTime = new Date();
			logger.info("{}<index update process> Process ended at ................ {}", core, df.format(endTime));
		} catch (MalformedURLException mue) {
			logger.error("Failed to assemble URL for HTTP POST", mue);
		} catch (IOException ioe) {
			logger.error("Failed to connect to the specified URL while trying to send HTTP POST", ioe);
		} catch (Exception e) {
			logger.error("Failed to send HTTP POST", e);
		}
	}

	public int getIntervalInt() {
		try {
			return Integer.parseInt(interval);
		} catch (NumberFormatException e) {
			logger.warn("Unable to convert 'interval' to number. Using default value (30) instead", e);
			return 30; // return default in case of error
		}
	}
}